const fs = require('fs');
const path = require('path');
const child_process = require('child_process');
const chalk = require('chalk');
const eachSeries = require('async/eachSeries');
const log = require('fancy-log');
const fsextra = require('fs-extra');
const jsyaml = require('js-yaml');
const { formatDate } = require('./common/utils');

const root_path = path.resolve(__dirname, '../');
const bin_path = path.resolve(__dirname, '../bin/');
const atlas_dir = '';
const rec_path = bin_path + '/' + atlas_dir + '.rec';
const suffix = 'json';

const publish_dir = fs.readFileSync(root_path + '/publish/gamehall').toString();
const publish_app_dir = publish_dir + '/www/files/game/deepseaglory';
const publish_app_index = publish_dir + '/app/template/game/deepseaglory/index.html';
const publish_version_dir = publish_dir + '/app/config/gameversion/deepseaglory.txt';

const commit_path = root_path + '/publish/commit.json';
const version_path =  path.resolve(__dirname, './version.yaml');


let args = process.argv.splice(2);

async function start() {
  let commit_data = await getLastCommitData().catch(err=> log.error(err));
    
  if (!fs.existsSync(rec_path)) 
    return log.error(chalk.red('\u8bf7\u5148\u53d1\u5e03\u8d44\u6e90'));
    
  let rec_version = parseInt(fs.statSync(rec_path).ctimeMs);
  let { last_commit_id } = commit_data;
  if (args && args.length) last_commit_id = args[0];

  let diff_list = await getDiff(last_commit_id);
  if (!checkAssetsChange(diff_list, rec_version)) 
    return log.error(chalk.red('\u8bf7\u5148\u53d1\u5e03\u8d44\u6e90'));

  await publishAssets(diff_list, rec_version);
  await publishScripts();

  eachSeries(['main.js', 'assets.js', 'define.js'], (item, next)=> {
    let p = `src/${item}`, act = 'M';
    fsextra.copy(`${root_path}/${p}`, `${publish_app_dir}/js/${item}`)
      .then(() => {
        log.info(chalk.green(`${act} success: ${p}`));
        next();
      }).catch(err => {
        log.info(chalk.red(`${act} failed: ${p}`));
        next();
      });
  });
    
}

setTimeout(start);

async function publishScripts() {
  const dgram = require('dgram');
  let cmd = (args && args.length) ? 'GET_ALL_CHUANKS': 'GET_CHANGE_CHUANKS';
  let message = Buffer.from(JSON.stringify({ cmd }));
  const client = dgram.createSocket('udp4');
  return new Promise(resolve=> {
    client.send(message, 53216, 'localhost', (err) => {
      client.on('message', (msg, rinfo) => {
        let { cmd, res: file_list } = JSON.parse(msg);
        switch (cmd) {
        case 'GET_CHANGE_CHUANKS':
        case 'GET_ALL_CHUANKS':
          eachSeries(file_list, (item, next) => {
            let p = `js/${item}`, act = 'M';
            fsextra.copy(`${bin_path}/${p}`, `${publish_app_dir}/${p}`)
              .then(() => {
                log.info(chalk.green(`${act} success: ${p}`));
                next();
              }).catch(err => {
                log.info(chalk.red(`${act} failed: ${p}`));
                next();
              });
          }, error=> {
            fs.writeFileSync(publish_version_dir, formatDate(new Date(), 'yyyyMMddhhmm'));
            resolve();
          });
          message = Buffer.from(JSON.stringify({ cmd: 'REST_CHANGE_CHUANKS' }));
          client.send(message, 53216, 'localhost');
          break;
        default:
          client.close();
          break;
        }
      });
    });
  });
}

async function publishAssets(diff_list, rec_version) {
  let commit_id = await getCommitId();
  let asset_map = await getAtlas();
  let version_map = await getVersionMap(asset_map);
  let commit_list = filterAsset(asset_map, diff_list);
  /* 同步复制到gameHall */
  return new Promise(reslove=> {
    let success_flag = true;
    eachSeries(commit_list, (item, next) => {
      let { act, name, type } = item;
      if (type == 0 || type == 1) { // pic || atlas
        let type_str = type ? `${atlas_dir}` : '';
        let p = `${type_str}${name}`;
        switch (act) {
        case 'M': //增加和修改
        case 'A':
          fsextra.copy(`${bin_path}/${p}`, `${publish_app_dir}/${p}`)
            .then(() => {
              version_map[p] = commit_id;
              log.info(chalk.green(`${act} success: ${p}`));
              next();
            }).catch(err => {
              success_flag = false;
              log.info(chalk.red(`${act} failed: ${p}`));
              next();
            });
          break;
        case 'D': //删除
          fsextra.remove(`${publish_app_dir}/${p}`)
            .then(() => {
              delete version_map[p];
              log.info(chalk.gray(`${act} success: ${p}`));
              next();
            }).catch(err => {
              success_flag = false;
              log.info(chalk.red(`${act} failed: ${p}`));
              next();
            });
          fsextra.remove(`${bin_path}/${p}`);
          break;
        default:
          next();
          break;
        }
      }
    }, error => {
      /* 更新gameHall js 版本号 */
      // updateScriptVersion(script_list, commit_id);
      let yamldoc = jsyaml.safeDump(version_map);
      fsextra.writeFileSync(version_path, yamldoc);
      fsextra.writeJsonSync(`${publish_app_dir}/version.json`, version_map);
      if (success_flag) {
        let commit_data = { last_commit_id: commit_id, last_rec_version: rec_version };
        fs.writeFileSync( commit_path, JSON.stringify(commit_data) );
        log.info(chalk.green.bold(`\u8D44\u6E90\u53D1\u5E03\u7248\u672C\u53F7: ${commit_id}`));
      }
      reslove();
    });
  });
}


/* 检查laya/**文件夹有变更, 需要发布laya资源 */
function checkAssetsChange(diff_list, rec_version) {
  let has_change_ui = diff_list.find(item=> {
    let name = item.substr(2);
    if (/^(laya\/)/.test(name)) {
      let ctimeMs, zname;
      zname = fs.existsSync(name)? name : name.replace(/(.+)\/.*$/, '$1');
      if (fs.existsSync(zname)) {
        ctimeMs = parseInt(fs.statSync(zname).ctimeMs);
        if (ctimeMs-rec_version>5000) return true;
      }
      return false;
    }
    return false;
  });
  if (has_change_ui) {
    log.info('change: ', has_change_ui);
    return false;
  }
  return true;
}

/* gameHall index.html js 版本号 */
function updateScriptVersion(script_list, commit_id) {
  let htmlcont = fs.readFileSync(publish_app_index).toString();
  let jsreg = /<!--version--startTag-->((?:.|(?:\r?\n))*)<!--version--endTag-->/;
  let oldscript = jsreg.exec(htmlcont);
  if (!oldscript || !oldscript.length>1) return;
  let version_inner = oldscript[1];
  script_list.forEach(jsname=> {
    if (!/\.js?/.test(jsname)) return;
    let reg = new RegExp(`(${jsname}[\?]v=)(.+)(\"|\'')`);
    version_inner = version_inner.replace(reg, `$1${commit_id}$3`);
  });
  htmlcont = htmlcont.replace(jsreg, `<!--version--startTag-->${version_inner}<!--version--endTag-->`);
  fs.writeFileSync(publish_app_index, htmlcont);
}


/* 读取version.json */
async function getVersionMap(asset_map) {
  return new Promise((reslove, reject)=> {
    let version_map;
    if (fs.existsSync(version_path)) {
      version_map = jsyaml.safeLoad(fs.readFileSync(version_path));
    } else {
      log.info(chalk.red(`\u4E0D\u5B58\u5728\u6587\u4EF6:${version_path}`));
    }
    reslove(version_map||{});
  });
}


/* diff_list中小图映射到图集 */
function filterAsset(asset_map, diff_list) {
  let commit_list = [];
  let asset_temp = [];
  if (!diff_list.length) {
    return log.info('\u6ca1\u6709\u5dee\u5f02');
  }
  diff_list.forEach(item => {
    let args = item.split('\t');
    let act = args[0].charAt(0).toUpperCase(), type = 0, name;
    switch (act) {
    case 'R':
      name = args[2];
      break;
    case 'A':
    case 'M':
    case 'D':
      name = args[1];
      break;
    default:
      log.info(chalk.yellow(item));
      return;
    }
    if (/^(laya\/assets\/)/.test(name)) {
      name = name.replace(/^(laya\/assets\/)/, '');
      if (!asset_temp.includes(name) && asset_map[name] && asset_map[name].type ) {
        let key = asset_map[name].key;
        if (asset_temp.includes(key)) return;
        asset_temp.push(name, key);
        type = 1;
        switch (act) {
        case 'D':
        case 'R':
          let dir = args[1].replace(/(.*)\/.*$/, '$1');
          key = dir.replace(/^(laya\/assets\/)/, '');
          if (!fs.existsSync(dir)) {
            commit_list.push({ act:'D', name: `${key}.png`, type });
            commit_list.push({ act:'D', name: `${key}.${suffix}`, type });
            return;
          }
          break;
        default:
          break;
        }
        commit_list.push({ act:'M', name: `${key}.png`, type });
        commit_list.push({ act:'M', name: `${key}.${suffix}`, type });
      } else if (!asset_temp.includes(name)) {
        let dir = args[1].replace(/(.*)\/.*$/, '$1'),
          key = dir.replace(/^(laya\/assets\/)/, '');
        if (act==='D'||act==='R') {
          let atlas = Object.keys(asset_map).find(v=> new RegExp(`${key}/.+`).test(name));
          if (atlas && !asset_temp.includes(key)) {
            type = 1;
            asset_temp.push(key);
            if (!fs.existsSync(dir)) {
              commit_list.push({ act:'D', name: `${key}.png`, type });
              commit_list.push({ act:'D', name: `${key}.${suffix}`, type });
            } else if (fs.existsSync( `${bin_path}/${key}.${suffix}`)) {
              commit_list.push({ act:'M', name: `${key}.png`, type });
              commit_list.push({ act:'M', name: `${key}.${suffix}`, type });
            }
            return;
          }
        }

        asset_temp.push(name);
        commit_list.push({ act, name, type });
      }
    }

  });
  return commit_list;
}



/** .rec区分图集 */
async function getAtlas() {
  return new Promise((relove, reject) => {
    let rec = fs.readFileSync(rec_path).toString().split('\n');
    let reg_d = /^D\ (.+)/,
      reg_p = /^P\ (.+)\ (.+\.png)/,
      reg_r = /^[A-Z]\ (.+)\ (.+\.png)/;
    let key, map = {};
    rec.forEach((str, i) => {
      if (reg_d.test(str)) { // 图集
        key = reg_d.exec(str)[1];
      } else if (str) { // 非图集
        let type = 0;
        let result = reg_p.exec(str);
        if (result) type = 1;
        else result = reg_r.exec(str);
        if (result) {
          let name = `${key}/${result[2]}`;
          let v = result[1];
          map[name] = { type, v, key };
        }
      }
    });
    relove(map);
  });
}

/* 获取最近的commit id */
async function getCommitId() {
  return new Promise((relove, reject) => {
    let commit_id = '';
    let git_rev = child_process.spawn('git', ['rev-parse', '--short', 'HEAD']);
    git_rev.stdout.on('data', (data) => {
      data = String(data);
      commit_id = data.replace(/\n/, '').trim();
    });
    git_rev.stderr.on('data', reject);
    git_rev.on('close', (code) => {
      relove(commit_id);
    });
  });
}

/* git diff 差异 */
async function getDiff(last_commit_id) {
  return new Promise((relove, reject) => {
    let git_diff = child_process.spawnSync('git', ['diff', last_commit_id, '--name-status', 'laya/'], {
      maxBuffer: 1024 * 1024
    });
    let data = git_diff.stdout;
    let diff_list = data.toString().split('\n').map(v => v.trim());
    relove(diff_list);
  });
}

/* 读取commit.json */
async function getLastCommitData() {
  return new Promise((resolve, reject) => {
    fs.readFile(commit_path, (err, data) => {
      if (err) reject(err);
      else resolve(JSON.parse(data.toString()));
    });
  });
}